const $ = document.querySelector.bind(document);

const UPLOADPHASENUMS = {
    select: 'select',
    progress: 'progress',
    result: 'result',
}

const selectors = {
    container: $("#upload-container"),
    file: $("#file"),
    upload: $("#upload-select"),
    progressBar: $("#progress-bar"),
    result: $("#upload-result"),
    imgPreview: $("#upload-img-preview"),
    cancel: $("#upload-cancel"),
}

function setPhase(phase) {
    selectors.container.className = `upload ${phase}`;
}

selectors.upload.addEventListener('click', () => {
    selectors.file.click();
})

selectors.result.addEventListener('click', () => {
    setPhase(UPLOADPHASENUMS.select);
})

selectors.cancel.addEventListener('click', () => {
    setPhase(UPLOADPHASENUMS.select);
    uploadCtr.index = Number.MAX_SAFE_INTEGER;
    uploadCtr.cancelFn();
})

const uploadCtr = {
    index: 0,
    total: 0,
    cancelFn: null // 取消上传的函数
}
selectors.file.addEventListener('change', async () => {
    setPhase(UPLOADPHASENUMS.progress)
    const file = selectors.file.files[0];
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onloadend = () => {
        selectors.imgPreview.src = reader.result;
    }

    const fileChuckList = sliceFile(file, 1024 * 2);
    const fileHashObj = await getFileHash(fileChuckList);
    uploadCtr.total = fileChuckList.length;
    /**
     * @description: 将要上传的文件分割成多个小块进行上传
     * @param {*} i
     * @return {*}
     */
    function _upload(i) {
        const file = {
            fileChuck: fileChuckList[i],
            fileChuckHash: fileHashObj.fileChucks[i],
            fileHash: fileHashObj.fileHash,
            isCompleted: i >= fileChuckList.length - 1
        }
        if (i >= fileChuckList.length) {
            setPhase(UPLOADPHASENUMS.result);
            return;
        }
        uploadCtr.cancelFn = upload(file,
            (percent) => {
                selectors.progressBar.style = `--percent:${percent * 100}%;`
            },
            (result) => {
                uploadCtr.index = i + 1;
                _upload(i + 1);
            }
        )
    }
    _upload(0)
})

/**
 * @description: 异步方式计算文件和文件块的哈希值
 * @param {*} fileChuckList
 * @param {*} fileChucks
 * @return {*}
 */
function getFileHash(fileChuckList) {
    return new Promise((resolve) => {
        const fileHash = {
            file: '',
            fileChucks: []
        }
        const spark = new SparkMD5();
        function _read(i) {
            if (i >= fileChuckList.length) {
                fileHash.file = spark.end();
                resolve(fileHash);
                return;
            }
            const reader = new FileReader();
            reader.onload = (e) => {
                const bytes = e.target.result;
                fileHash.fileChucks.push(SparkMD5.ArrayBuffer.hash(bytes))
                spark.append(bytes);
                _read(i + 1);
            }
            reader.readAsArrayBuffer(fileChuckList[i]);
        }
        _read(0);
    })
}
/**
 * @description: 上传文件到服务器
 * @param {*} file
 * @param {*} onProgress 上传过程中执行的回调
 * @param {*} onFinish 上传完成后执行的回调
 * @return {*}
 */
function upload(file, onProgress, onFinish) {
    const formData = new FormData();
    formData.append('fileChuck', file.fileChuck);
    formData.append('fileChuckHash', file.fileChuckHash);
    formData.append('fileHash', file.fileHash);
    formData.append('isCompleted',file.isCompleted)
    const xhr = new XMLHttpRequest();
    xhr.open('POST', '/upload');
    xhr.addEventListener("readystatechange", () => {
        if (xhr.readyState === 4 && xhr.status === 200) {
            onFinish(JSON.parse(xhr.responseText));
        }
    })
    xhr.addEventListener("progress", (e) => {
        let percent = (uploadCtr.index + e.loaded / e.total) / uploadCtr.total;
        onProgress(percent);
    })
    xhr.send(formData);
    return () => {
        xhr.abort();
    }
}

/**
 * @description: 将一个文件分割成多个 chuck
 * @param {*} file
 * @param {*} size
 * @return {*}
 */
function sliceFile(file, size) {
    const fileChuckList = [];
    let curSize = 0;
    let fileSize = file.size;
    while (curSize <= fileSize) {
        let end = (curSize + size <= fileSize) ? (curSize + size) : fileSize;
        fileChuckList.push(file.slice(curSize, end));
        curSize += size;
    }
    return fileChuckList;
}
